home *** CD-ROM | disk | FTP | other *** search
- // GeoClient.m
- // By Charles G. Fleming, Educational Computing Services, Allegheny College.
- // You may freely copy, distribute and reuse this code.
- // Allegheny College and the author disclaim any warranty of any kind,
- // expressed or implied, as to its fitness for any particular use.
- // This work was partially supported by a grant from the Vira Heinz Endowment.
-
-
- #import "GeoClient.h"
- #import "Subprocess.h"
-
- #import <appkit/Panel.h>
- #import <appkit/Application.h>
- #import <appkit/TextField.h>
- #import <appkit/PopUpList.h>
- #import <appkit/MenuCell.h>
- #import <appkit/ScrollView.h>
- #import <appkit/Text.h>
- #import <appkit/Button.h>
-
- #import <objc/Storage.h>
- #import <objc/Object.h>
-
- #import <strings.h>
- #import <string.h>
- #import <stdlib.h>
-
- @implementation GeoClient
-
- int stuffer(struct dataRecord* , char *);
-
- - appDidInit:sender
- {
- subprocess = [[Subprocess allocFromZone:[self zone]] init:"/bin/csh" withDelegate:self andPtySupport:YES andStdErr:YES];
- [subprocess send:"telnet martini.eecs.umich.edu 3000"];
-
- records = [[Storage allocFromZone:[self zone]] initCount:10
- elementSize:sizeof(struct dataRecord)
- description:"{[50c][50c][50c][50c][50c][50c][50c][50c][50c][50c][50c][50c][50c][50c][50c][50c][50c]}"];
-
- // Set up the pull down lists.
- statesPullDownList = [statesPullDownListButton target];
- [statesPullDownList setTarget:self];
- [statesPullDownList setAction:@selector(selectState:)];
- moreStatesPullDownList = [moreStatesPullDownListButton target];
- [moreStatesPullDownList setTarget:self];
- [moreStatesPullDownList setAction:@selector(selectState:)];
-
- // Display a message until login is complete.
- [NXApp runModalFor:loginMessageWindow];
- return self;
- }
-
- // Query the database for information on a particular city.
- - query:sender
- {
- char *cityAndState;
-
- // Enable the button only after the query is finished.
- [sender setEnabled:NO];
-
- // So that we will receive text did change messages from our text fields.
- [self makeFirstResponder:self];
-
- // Reset the pull down list, clear the data records in the storage object and clear the
- // text fields.
- [self resetPullDownList];
- [records empty];
- [self clearTextFields];
-
- cityAndState = malloc(strlen([cityTextField stringValue])+strlen([stateTextField stringValue])+3);
-
- // Build the query string for the database server. The query string will be "City, State" or "City".
- if(strlen([stateTextField stringValue]) > 0)
- sprintf(cityAndState, "%s, %s", [cityTextField stringValue],[stateTextField stringValue]);
- else
- strcpy(cityAndState, [cityTextField stringValue]);
-
- // Send the query to the subprocess object.
- [subprocess send:cityAndState];
-
- // Searching message window.
- [searchingMessageWindow makeKeyAndOrderFront:self];
- free(cityAndState);
- return self;
- }
-
- // Put the information for the state selected from the pull down list into the text fields.
- - selectState:sender
- {
- int tag;
- struct dataRecord *newRecord;
-
- // Get the record for the selected state.
- tag = [[sender selectedCell] tag];
- newRecord = (struct dataRecord *)[records elementAt:tag];
-
- // Put the information into the text fields.
- [stateTextField setStringValue:newRecord->stateName];
- [countyCodeTextField setStringValue:newRecord->countyCode];
- [countyNameTextField setStringValue:newRecord->countyName];
- [stateAbbrTextField setStringValue:newRecord->stateAbbr];
- [stateNameTextField setStringValue:newRecord->stateName];
- [nationAbbrTextField setStringValue:newRecord->nationAbbr];
- [nationNameTextField setStringValue:newRecord->nationName];
- [areaCodeTextField setStringValue:newRecord->areaCode];
- [elevationTextField setStringValue:newRecord->elevation];
- [featureCodeTextField setStringValue:newRecord->featureCode];
- [featureNameTextField setStringValue:newRecord->featureName];
- [latitudeTextField setStringValue:newRecord->latitude];
- [longitudeNameTextField setStringValue:newRecord->longitude];
- [censusTextField setStringValue:newRecord->census];
- [remarkTextField setStringValue:newRecord->remark];
- [timeZoneTextField setStringValue:newRecord->timeZone];
- [[zipScrollView docView] setText:newRecord->zip];
- return self;
- }
-
- // Clear all of the text fields.
- - clearTextFields
- {
- [countyCodeTextField setStringValue:""];
- [countyNameTextField setStringValue:""];
- [stateAbbrTextField setStringValue:""];
- [stateNameTextField setStringValue:""];
- [nationAbbrTextField setStringValue:""];
- [nationNameTextField setStringValue:""];
- [areaCodeTextField setStringValue:""];
- [elevationTextField setStringValue:""];
- [featureCodeTextField setStringValue:""];
- [featureNameTextField setStringValue:""];
- [latitudeTextField setStringValue:""];
- [longitudeNameTextField setStringValue:""];
- [censusTextField setStringValue:""];
- [remarkTextField setStringValue:""];
- [timeZoneTextField setStringValue:""];
- [[zipScrollView docView] setText:""];
- return self;
- }
-
- // Remove the entries from the pull down lists.
- - resetPullDownList
- {
- int statesCount, state;
-
- statesCount = [statesPullDownList count];
- for(state = statesCount-1; state > 0; state--)
- [statesPullDownList removeItemAt:state];
-
- statesCount = [moreStatesPullDownList count];
- for(state = statesCount-1; state > 0; state--)
- [moreStatesPullDownList removeItemAt:state];
- return self;
- }
-
- - textDidChange:textObject
- {
- TextField *textField;
-
- // Clear the text fields.
- [self clearTextFields];
-
- // Reset the pull down list.
- [self resetPullDownList];
- [statesPullDownListButton setEnabled:NO];
- [moreStatesPullDownListButton setEnabled:NO];
-
- // Emtpy all city records.
- [records empty];
-
- textField = [textObject delegate];
- if(textField == cityTextField)
- [stateTextField setStringValue:""];
- return self;
- }
-
- - appWillTerminate:sender
- {
- [subprocess send:"bye"];
- [subprocess terminate:self];
- return self;
- }
-
- // Process the output from the subprocess object.
- - subprocessOutput:(char *)buffer
- {
- static char *message;
- char *period, *start, *end;
- static BOOL firstTime = YES;
- static BOOL login = YES;
- int cityCount;
- MenuCell *menuCell;
-
- // Malloc a message buffer only once.
- if(firstTime)
- {
- message = malloc(10000);
- firstTime = NO;
- }
-
- // Process a login.
- strcat(message, buffer);
- if(login)
- {
- period = rindex(message, '.');
- if(period )
- if(*(period - 1) == '\n')
- {
- [loginMessageWindow close];
- [NXApp stopModal];
- [queryButton setEnabled:YES];
- bzero(message, strlen(message)+1);
- *(message+1) = '\0';
- login = NO;
- }
- }
- // Process the data returned from the database server.
- else
- {
- // Process the records only after all buffers have arrived.
- if(strstr(message, "\n."))
- {
- // No cities found.
- if(!strstr(message, "\n0"))
- {
- bzero(message, strlen(message)+1);
- [searchingMessageWindow close];
- [queryButton setEnabled:YES];
- }
- // Process the cities.
- else
- {
- // Process all records but the last one (states).
- cityCount = 0;
- start = strstr(message, "\n0")+1;
- while(end = strstr(start, "\n0"))
- {
- *end = '\0';
- // Clear out the record and then stuff all of the information into it.
- bzero(&record, sizeof(struct dataRecord));
-
- if(stuffer(&record, start))
- {
- if(cityCount < 39)
- {
- // Check for duplicate states. The data base has some duplicates.
- while( [statesPullDownList indexOfItem:record.stateName] != -1)
- strcat(record.stateName, " ");
-
- // Get the state and add a button to the pulldown list.
- menuCell = [statesPullDownList addItem:record.stateName];
- [menuCell setTag:cityCount];
- }
- else
- {
- // Check for duplicate states. The data base has some duplicates.
- while( [moreStatesPullDownList indexOfItem:record.stateName] != -1)
- strcat(record.stateName, " ");
-
- // Get the state and add a button to the pulldown list.
- menuCell = [moreStatesPullDownList addItem:record.stateName];
- [menuCell setTag:cityCount];
- }
- [records addElement:&record];
- cityCount++;
- }
- start = end+1;
- }
-
- // Now process the last record (state).
- end = strstr(start, "\n.");
- *end = '\0';
-
- // Clear out the record and then stuff all of the information into it.
- bzero(&record, sizeof(struct dataRecord));
- if(stuffer(&record, start))
- {
- if(cityCount < 39)
- {
- // Check for duplicate states. The data base has some duplicates.
- while( [statesPullDownList indexOfItem:record.stateName] != -1)
- strcat(record.stateName, " ");
-
- // Get the state and add a button to the pulldown list.
- menuCell = [statesPullDownList addItem:record.stateName];
- [menuCell setTag:cityCount];
- }
- else
- {
- // Check for duplicate states. The data base has some duplicates.
- while( [moreStatesPullDownList indexOfItem:record.stateName] != -1)
- strcat(record.stateName, " ");
-
- // Get the state and add a button to the pulldown list.
- menuCell = [moreStatesPullDownList addItem:record.stateName];
- [menuCell setTag:cityCount];
- }
- [records addElement:&record];
- cityCount++;
- }
- if(cityCount > 0)
- [statesPullDownListButton setEnabled:YES];
- else
- [statesPullDownListButton setEnabled:NO];
- if(cityCount > 38)
- [moreStatesPullDownListButton setEnabled:YES];
- else
- [moreStatesPullDownListButton setEnabled:NO];
- bzero(message, strlen(message)+1);
- [searchingMessageWindow close];
- [queryButton setEnabled:YES];
- }
- }
- }
- return self;
- }
-
- // This is sent when the connection is broken, or when we quit the application.
- - subprocessDone
- {
- NXRunAlertPanel("BYE", "You are no longer connected to the remote machine.",
- NULL, NULL, NULL);
- return self;
- }
-
- // Display any error messages received.
- - subprocessError:(const char *)errorString
- {
- [errorMessageWindow makeKeyAndOrderFront:self];
- [[errorMessageScrollView docView] setText:errorString];
- return self;
- }
-
- // Takes each string, corresponding to a city and specific state, and puts the information
- // into a record.
- int stuffer(struct dataRecord *record , char *entry)
- {
- char *zero, *start, *copy, *linefeed, *string, *nextSpace;
- BOOL stateFound = NO, done = NO;
- int firstChar, blank;
-
- copy = malloc(strlen(entry) + 1);
- strcpy(copy, entry);
-
- // Find the city field and put it into the structure.
- zero = index(copy, '0')+2;
- linefeed = index(zero, '\n');
- *(linefeed) = '\0';
- strcpy(record->city, zero);
-
- // Get the fields and put them into the structure.
- start = linefeed +1;
- while(!done)
- {
- // Put a null terminator at the end of each line before processing the line.
- linefeed = index(start, '\n');
- *linefeed = '\0';
-
- // Based on the first character, put the field into the structure.
- firstChar = (int)*(start);
-
- switch(firstChar)
- {
- case '1':
- // Get the county code.
- string = index(start, ' ')+1;
- nextSpace = index(string, ' ');
- *nextSpace = '\0';
- strcpy(record->countyCode, string);
-
- // Get the county name.
- string = nextSpace+1;
- strcpy(record->countyName, string);
- break;
- case '2':
- stateFound = YES;
- // Get the state abbreviation.
- string = index(start, ' ')+1;
- nextSpace = index(string, ' ');
- *nextSpace = '\0';
- strcpy(record->stateAbbr, string);
-
- // Get the state name.
- string = nextSpace+1;
- strcpy(record->stateName, string);
- break;
- case '3':
- // Get the nation abbreviation.
- string = index(start, ' ')+1;
- nextSpace = index(string, ' ');
- *nextSpace = '\0';
- strcpy(record->nationAbbr, string);
-
- // Get the nation name.
- string = nextSpace+1;
- strcpy(record->nationName, string);
- break;
- case 'A':
- // Get the area code.
- string = index(start, ' ')+1;
- strcpy(record->areaCode, string);
- break;
- case 'E':
- // Get the elevation.
- string = index(start, ' ')+1;
- strcpy(record->elevation, string);
- break;
- case 'F':
- // Get the feature code.
- string = index(start, ' ')+1;
- nextSpace = index(string, ' ');
- *nextSpace = '\0';
- strcpy(record->featureCode, string);
-
- // Get the feature name.
- string = nextSpace+1;
- strcpy(record->featureName, string);
- break;
- case 'L':
- // Get the latitude.
- string = index(start, ' ')+1;
- nextSpace = index(string, ' ');
- for(blank = 0; blank < 3; blank++)
- nextSpace = index(nextSpace+1, ' ');
- *nextSpace = '\0';
- strcpy(record->latitude, string);
-
- // Get the longitude.
- string = nextSpace+1;
- strcpy(record->longitude, string);
- break;
- case 'P':
- // Get the census.
- string = index(start, ' ')+1;
- strcpy(record->census, string);
- break;
- case 'R':
- // Get the remark.
- string = index(start, ' ')+1;
- strcpy(record->remark, string);
- break;
- case 'T':
- // Get the time zone.
- string = index(start, ' ')+1;
- strcpy(record->timeZone, string);
- break;
- case 'Z':
- // Get the zip codes.
- string = index(start, ' ')+1;
- strcat(record->zip, string);
- break;
- }
-
- if(index(linefeed+1, '\n'))
- start = linefeed + 1;
- else
- done = YES;
- }
- free(copy);
- return stateFound;
- }
-
- @end
-